<?php

namespace app\admin\command;


use think\console\Command;
use think\console\Input;
use think\console\Output;
/**
 * zhoujianlei
 * 监控日志 有变化发邮件通知
 */
class MonitorLog extends Command
{
    const LOG_FILE_PATH = '/tmp/prd_monitor_log_line.log';
    const ERROR_LOG_PATH = '/tmp/prd_monitor_error.log';
    const READ_LINE = 1000; //读取的默认行数

    protected function configure()
    {
        $this->setName('MonitorLog')->setDescription('监控日志 有变化发送邮件 ');
    }

    protected function execute(Input $input, Output $output)
    {

        $output->writeln('开始...');
        $this->log('开始...');
        try {
            //1. 从文件读出上次读取日志的位置
            $last_time_log_line = $this->getLastTimeLogLine(self::LOG_FILE_PATH);

            $this->log('上次读取文件行数:' . $last_time_log_line);

            //2. 读取日志行数
            // monitorLogPath
            $log_path = config('monitorLogPath');
            $log_file_line = $this->getFileLine($log_path);

            //3. 将本次日志行数写入文件
            file_put_contents(self::LOG_FILE_PATH, $log_file_line);

            //4. 比较日志行数与上次有无变化
            //日志内容为空 不读取
            if ($log_file_line === 0) {
                $this->log('日志内容为空 不读取 不发通知邮件 结束...' . PHP_EOL);
                $output->writeln('日志内容为空 不读取 不发通知邮件 结束...');
                return;
            }
            if ($last_time_log_line === 0) {
                //上一次没有记录的话 日志超过默认行数 读最后1000行 不超过1000行 全部读出
                if ($log_file_line >= self::READ_LINE) {
                    $last_time_log_line = $log_file_line - self::READ_LINE;
                    $read_line = self::READ_LINE;
                } else {
                    $read_line = $log_file_line;
                }
            } else {
                $read_line = $log_file_line - $last_time_log_line;
            }
            if ($read_line === 0) {
                $this->log('要读取的行数:' . $read_line . '结束...' . PHP_EOL);
                $output->writeln('要读取的行数:' . $read_line . ' 结束...');
                return;
            }

            //5. 将变化的行数读取出来 拼接
            $content = $this->getFileContent($log_path, $last_time_log_line, $read_line);
//            $content = $this->getFileContent($log_path, 1, 100);
            $this->log(print_r($content, true));

            $log_content = implode(PHP_EOL, $content);
            //$this->log($log_content);
            $email_content = '日志系统监控结果如下:';
            $email_content .= '<div style="height: 600px;overflow: auto;background: #f5f5f5;font-size: 12px; color: #000000;word-break: break-word;margin: 10px;">';
            $email_content .= nl2br($log_content);
            $email_content .= '</div>';
            //6. 发送邮件
            $this->sendEmail($email_content);
            $output->writeln('结束了!!!');
            $this->log(' ^_^ 本次结束啦...' . PHP_EOL);
        } catch (ErrorException $e) {
            $output->writeln('w(ﾟДﾟ)w 报错了 ERROR: ==> - file:' . $e->getFile() . ' - line:' . $e->getLine() . ' - message:' . $e->getMessage());
        }
    }

    /**
     * 获取上一次日志行数
     * @param $file_path
     */
    private function getLastTimeLogLine($file_path): int
    {
        if (empty($file_path)) {
            return 0;
        }
        if (file_exists($file_path) && is_file($file_path)) {
            $last_time_log_line = file_get_contents($file_path);
            if ($last_time_log_line) {
                $this->log('上次读取文件行数:' . (int)$last_time_log_line);
                return (int)$last_time_log_line;
            } else {
                $this->log('未读取到文件内容:' . $file_path);
            }
        } else {
            $this->log('文件不存在:' . $file_path);
        }
        return 0;
    }

    /**
     * 获取文件总行数
     * @param $file_path
     * @return int
     */
    private function getFileLine($file_path): int
    {
        $line = 0;
        if (file_exists($file_path) && is_file($file_path)) {
            $fp = fopen($file_path, 'r') or die('打开日志文件错误!');
            if ($fp) {
                //获取文件的一行内容
                while (!feof($fp)) {
                    $buffer = fgets($fp, 40960);
                    ++$line;
                }
                //关闭文件
                fclose($fp);
            }
        } else {
            $this->log('文件 不存在 请检查文件路径:' . $file_path);
        }
        //总行数
        $this->log('总行数:' . $line);
        return $line;
    }

    /**
     * 发送邮件
     * @throws \phpmailerException
     */
    private function sendEmail($content = ''): bool
    {
        vendor('phpmailer.PHPMailerAutoload');

        $host = config('email.host');
        $user_name = config('email.user_name');
        $password = config('email.password');
        $from_name = config('email.from_name') ?: '系统日志监控';
        $title = config('email.title') ?: '日志监控';
        $port = config('email.port') ?: 25;
        $address_array = config('email.address');

        $mail = new \PHPMailer();

        // 设置邮件的字符编码，若不指定，则为'UTF-8'
        $mail->CharSet = 'UTF-8';
        // 设置PHPMailer使用SMTP服务器发送Email
        $mail->IsSMTP();
        // 设置SMTP服务器。
        $mail->Host = $host;
        // 设置为"需要验证" ThinkPHP 的config方法读取配置文件
        $mail->SMTPAuth = true;

        // 设置邮件头的From字段。
        $mail->From = $user_name;
        // 设置发件人名字
        $mail->FromName = $from_name;

        // 添加收件人地址，可以多次使用来添加多个收件人
//        $mail->AddAddress('zhoujianlei@yljr.com', '周建雷');
        foreach ($address_array as $address) {
            $mail->AddAddress($address);
        }
        $mail->addBCC('378848184@qq.com');

        // 设置邮件正文
        $mail->Body = $content;

        // 设置邮件标题
        $mail->Subject = $title;

        //设置html发送格式
        $mail->isHTML(true);
        // 设置用户名和密码。
        $mail->Username = $user_name;
        $mail->Password = $password;
        // 端口
        $mail->Port = $port;
        // 发送邮件。
        if ($mail->Send()) {
            $this->log(print_r($address_array, true));
            $this->log('邮件发送成功...');
            return true;
        } else {
            $this->log('发送邮件失败!');
            return false;
        }

    }

    /**
     * 读取日志内容
     * @param $file
     * @param int $start_line
     * @param int $line_num
     * @param int $length
     * @return array
     */
    private function getFileContent($file, $start_line = 1, $line_num = 1, $length = 40960): array
    {
        $content = []; // 初始化返回
        $i = 0; // 行数
        $read_line = 0;// 读的行数

        $handle = @fopen($file, 'r') or die('打开文件错误!');
        if ($handle) {
            while (!feof($handle)) {
                $buffer = fgets($handle, $length);
                ++$i;
                if ($i >= $start_line) {
                    ++$read_line;
                    if ($read_line <= $line_num) {
                        $content[] = $buffer;
                    }

                }

            }
            fclose($handle);
        }
        return $content;
    }

    /**
     * 记录日志
     * @param string $message
     */
    private function log($message = ''): void
    {
        error_log('[' . date('Y-m-d H:i:s') . '] -- ' . $message . PHP_EOL, 3, '/tmp/prd_monitor_' . date('Y-m-d') . '.log');
    }

}